home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ue312src.zip / MSWSYS.C < prev    next >
C/C++ Source or Header  |  1993-04-21  |  35KB  |  1,147 lines

  1. /* The routines in this file provide support for various OS-related
  2.    functions under the Microsoft Windows environment on an IBM-PC or
  3.    compatible computer.
  4.  
  5.    Must be compiled with Borland C++ 2.0 or MSC 6.0 or later versions.
  6.  
  7.    It should not be compiled if the WINDOW_MSWIN symbol is not set */
  8.  
  9. #define        OEMRESOURCE    1   /* to have access to OBM_CLOSE from windows.h */
  10. #include    "estruct.h"
  11. #include    "elang.h"
  12. #include    <stdio.h>
  13. #include    <time.h>
  14. #include    "eproto.h"
  15. #include    "edef.h"
  16.  
  17. #include    "mswin.h"
  18.  
  19. #if WINDOW_MSWIN32
  20. #include <setjmp.h>
  21. #endif
  22.  
  23. #define MAXPARAM    10      /* max command line parameters */
  24. #define TXTSIZ      NFILEN  /* all purpose string length */
  25. #define T_SLEEP      1      /* Sleep timer ID */
  26.  
  27. /* message codes for EmacsBroadcastMsg */
  28. #define EMACS_PROCESS   1
  29. #define EMACS_STARTING  1
  30. #define EMACS_ENDING    2
  31.  
  32. /* variables */
  33. static char     *argv[MAXPARAM];
  34. static int      argc;
  35.  
  36. static char FrameClassName [] = PROGNAME ":frame";
  37.  
  38. #if WINDOW_MSWIN32
  39. /* The Catch/Throw API is replaced by setjmp/longjmp */
  40. static jmp_buf ExitCatchBuf;
  41. #define Throw(buf,n) longjmp(buf,n)
  42. #define Catch(buf)   setjmp(buf)
  43. #else
  44. static CATCHBUF ExitCatchBuf;
  45. #endif
  46.  
  47. static int  Sleeping = 0;           /* flag for TakeANap() */
  48. static int  TimeSlice;
  49. static BOOL LongOperation = FALSE;  /* for longop() and GetCharacter() */
  50. static BOOL HourglassOn = FALSE;    /* between SetHourglass & UpdateCursor */
  51. static BOOL SystemModal = FALSE;    /* indicates that the Yes/No message
  52.                        box should be system-modal */
  53.  
  54. static UINT EmacsBroadcastMsg;
  55. static DWORD BroadcastVal;          /* used by the EmacsBroadcast function */
  56.  
  57. static WNDPROC  MDIClientProc;
  58.  
  59. #if GRINDERS != 0
  60. static HCURSOR  hRealHourglass;
  61. #endif
  62.  
  63. /* prototypes */
  64. static void  PASCAL MessageLoop (BOOL WaitMode);
  65. static BOOL  PASCAL UpdateCursor (HWND hWnd, UINT wParam, LONG lParam);
  66. static void  PASCAL SetHourglass (BOOL hg);
  67.  
  68. /* timeset: return a system-dependant time string */
  69. /* =======                                        */
  70. char *PASCAL timeset()
  71.  
  72. {
  73.     register char *sp;    /* temp string pointer */
  74.     time_t buf;        /* time data buffer */
  75.  
  76.     time(&buf);
  77.     sp = ctime(&buf);
  78.     sp[strlen(sp)-1] = 0;
  79.     return(sp);
  80. }
  81.  
  82. /* longop:    to be called regularly while a long operation is in progress */
  83. /* ========                                                                */
  84.  
  85. PASCAL longop (int f)
  86.  
  87. /* f is TRUE to set long operation status and FALSE to reset that status */
  88. /* when a long operation is signaled at least twice, the hourglass
  89.    cursor comes up */
  90. /* While the long operation is in progress, this function runs the
  91.    message loop approximately every 100 ms to yield to other
  92.    applications and allow a reduced set of commands (among which quit)
  93.    to be input by the user */
  94. {
  95.     static DWORD    LastYield;  /* time of last yield in milliseconds */
  96.     
  97.     if (f) {
  98.         if (!LongOperation) {
  99.             LongOperation = TRUE;
  100.             LastYield = GetCurrentTime ();
  101.         }
  102.     else {  /* 2nd (or more) call in a row, let's yield if enough
  103.            time has elapsed */
  104.         DWORD   Time;
  105.  
  106.         if ((Time = GetCurrentTime ()) - LastYield >= TimeSlice) {
  107. #if GRINDERS != 0
  108.                 if (++GrinderIndex >= GRINDERS) GrinderIndex = 0;
  109. #endif
  110.         SetHourglass (TRUE);
  111.             LastYield = Time;
  112.             MessageLoop (FALSE);
  113.         }
  114.     }
  115.     }
  116.     else {
  117.     if (LongOperation) {
  118.         LongOperation = FALSE;
  119.         SetHourglass (FALSE);
  120.     }
  121.     }
  122. } /* longop */
  123.  
  124. /* mlyesno: ask a yes/no question */
  125. /* =======                        */
  126.  
  127. PASCAL mlyesno (char *prompt)
  128.  
  129. /* This function replaces the mlyesno from input.c. Instead of asking a
  130.    question on the message line, it pops up a message box */
  131. {
  132.     if (MessageBox (hFrameWnd, prompt, ProgName,
  133.                     MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION |
  134.             (SystemModal ? MB_SYSTEMMODAL : 0)) == IDYES) return TRUE;
  135.     return FALSE;
  136. } /* mlyesno */
  137.  
  138. /* mlabort: display a serious error message (proposes abort) */
  139. /* =======                                                   */
  140.  
  141. VOID PASCAL NEAR mlabort (char *s)
  142. {
  143.     char    text[NSTRING];  /* hopefully sufficient! */
  144.     
  145.     while (*s == '%') s++;  /* remove those strange % signs in some messages */
  146.     strcpy (text, s);
  147.     strcat (text, "\tAbort ");
  148.     strcat (text, ProgName);
  149.     strcat (text, " ?");
  150.     if (MessageBox (hFrameWnd, text, NULL,
  151.                     MB_YESNO | MB_DEFBUTTON2 |
  152.                     MB_ICONHAND | MB_APPLMODAL) == IDYES) {
  153.         eexitval = -1;
  154.         Throw (ExitCatchBuf, ABORT);
  155.      }
  156. } /* mlabort */
  157.  
  158. /* WinInit: all the window initialization crap... */
  159. /* =======                                        */
  160.  
  161. BOOL FAR PASCAL WinInit (LPSTR lpCmdLine, int nCmdShow)
  162.  
  163. /* returns FALSE if failed init */
  164. {
  165.     WNDCLASS    wc;
  166.     HMENU       hSysMenu;
  167.     char        text [TXTSIZ];
  168.     POINT       InitPos;
  169.     int         Colors;
  170.     int         i;
  171.     static char HomeDir [NFILEN] = "HOME=";
  172.     WORD        w;
  173.  
  174.     InitializeFarStorage ();
  175. #if WINDOW_MSWIN32
  176.     Win386Enhanced = FALSE;
  177.     Win31API = TRUE;
  178. #else
  179.     Win386Enhanced = GetWinFlags () & WF_ENHANCED;
  180.     w = LOWORD(GetVersion());
  181.     Win31API = ((w & 0xFF) > 3) || ((w >> 8) >= 10);
  182. #endif
  183.  
  184.     GetProfileString (ProgName, "HelpFile", "", text, TXTSIZ);
  185.     if (text[0] != '\0') MainHelpFile = copystr (text);
  186.     else MainHelpFile = NULL;   /* default supplied below... */
  187.     MainHelpUsed = FALSE;
  188.     HelpEngineFile[0] = '\0';
  189.  
  190.     i = GetModuleFileName (hEmacsInstance, text, NFILEN-9);
  191.     for (; i >= 0; i--) {
  192.         if (text[i] == '\\') {
  193.             break;
  194.         }
  195.     }
  196.     if (!MainHelpFile) {    /* default WinHelp file name */
  197.         if (i > 0) text[i+1] = '\0';
  198.         else text[0] = '\0';
  199.         strcat (text, "mewin.hlp");
  200.         MainHelpFile = copystr (text);
  201.     }
  202.     if (i > 0) {
  203.         text[i] = '\0';
  204.         strcat (HomeDir, text);
  205.         putenv (HomeDir);   /* set HOME environment var to point to
  206.                    MicroEMACS executable directory */
  207.     }
  208.  
  209.     TimeSlice = GetProfileInt (ProgName, "TimeSlice", 100);
  210.  
  211.     Colors = GetProfileInt (ProgName, "Colors", 0);
  212.  
  213.     GetProfileString (ProgName, "InitialSize", "", text, TXTSIZ);
  214.     strlwr (text);
  215.     if (strstr (text, "optimize")) {
  216.     InitPos.x = InitPos.y = 0;
  217.     }
  218.     else {
  219.     InitPos.x = InitPos.y = CW_USEDEFAULT;
  220.     }
  221.     if (nCmdShow == SW_SHOWNORMAL) {
  222.     if (strstr (text, "maximize")) {
  223.         nCmdShow = SW_SHOWMAXIMIZED;
  224.     }
  225.     else if (strstr (text, "minimize") || strstr (text, "icon")) {
  226.         nCmdShow = SW_SHOWMINNOACTIVE;
  227.     }
  228.     }
  229.  
  230.     /*-Register the broadcast message */
  231.     strcpy (text, ProgName);
  232.     strcat (text, ":Broadcast_1.1");
  233.     EmacsBroadcastMsg = RegisterWindowMessage(text);
  234.  
  235.     /*-Register the frame window class */
  236.     wc.style        = 0;
  237.     wc.lpfnWndProc  = (void*)&FrameWndProc;
  238.     wc.cbClsExtra   = 0;
  239.     wc.cbWndExtra   = FRMWNDEXTRA;
  240.     wc.hInstance    = hEmacsInstance;
  241.     wc.hIcon        = LoadIcon (hEmacsInstance, "AppIcon");
  242.     wc.hCursor      = NULL;
  243.     wc.hbrBackground= (HBRUSH)(COLOR_WINDOW + 1);
  244.     wc.lpszMenuName = "Menu";
  245.     wc.lpszClassName= FrameClassName;
  246.     RegisterClass (&wc);
  247.  
  248.     /*-Register the MDI child (screen) window class */
  249.     strcpy (text, ProgName);
  250.     strcat (text, "_screen");
  251.     ScreenClassName = copystr(text);
  252.     wc.style        = 0;
  253.     wc.lpfnWndProc  = (void*)&ScrWndProc;
  254.     wc.cbClsExtra   = 0;
  255.     wc.cbWndExtra   = SCRWNDEXTRA;
  256.     wc.hInstance    = hEmacsInstance;
  257.     wc.hIcon        = LoadIcon (hEmacsInstance, "ScreenIcon");
  258.     wc.hCursor      = NULL;
  259.     wc.hbrBackground= (HBRUSH)(COLOR_WINDOW + 1);
  260.     wc.lpszMenuName = NULL;
  261.     wc.lpszClassName= ScreenClassName;
  262.     RegisterClass (&wc);
  263.  
  264.     /*-Create the frame window */
  265.     hFrameWnd = CreateWindow (FrameClassName,       /* class */
  266.                   ProgName,             /* title */
  267.                   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  268.                   InitPos.x,            /* positions */
  269.                   InitPos.y,
  270.                   CW_USEDEFAULT,        /* dimensions */
  271.                   CW_USEDEFAULT,
  272.                   NULL,                 /* parent handle */
  273.                   NULL,                 /* menu */
  274.                   hEmacsInstance,
  275.                   NULL);
  276.  
  277.     if (hFrameWnd == 0) return FALSE;
  278.  
  279.     if (Colors == 0) {
  280.         /* try to detect monochrome displays */
  281.     HDC     hDC;
  282.  
  283.     hDC = GetDC (hFrameWnd);
  284.         ColorDisplay = (GetDeviceCaps (hDC, NUMCOLORS) > 2);
  285.     ReleaseDC (hFrameWnd, hDC);
  286.     }
  287.     else {
  288.         ColorDisplay = (Colors > 2);
  289.     }
  290.  
  291.     ShowWindow (hFrameWnd, nCmdShow);
  292.  
  293.     hScreenCursor = LoadCursor (hEmacsInstance, "ScreenCursor");
  294.     hTrackCursor = hScreenCursor;
  295.     hNotQuiescentCursor = LoadCursor (hEmacsInstance,
  296.                                       "NotQuiescentCursor");
  297. #if GRINDERS == 0
  298.     hHourglass = LoadCursor (NULL, IDC_WAIT);
  299. #else
  300.     strcpy (text, "Grinder1");
  301.     for (i = 0; i < GRINDERS; i++) {
  302.         text[7] = (char)i + '1';    /* this assumes GRINDERS < 10 */
  303.         GrinderCursor[i] = LoadCursor (hEmacsInstance, text);
  304.     }
  305.     hRealHourglass = LoadCursor (NULL, IDC_WAIT);
  306.     GrinderIndex = 0;
  307.     hHourglass = GrinderCursor[0];
  308. #endif
  309.  
  310.     in_init ();                 /* sets up the input stream */
  311.  
  312.     argv [0] = ProgName;        /* dummy program name */
  313.     {
  314.     register char   *s;
  315.  
  316.     argc = 1;
  317.     s = copystr (lpCmdLine);
  318.     while (*s != '\0') {
  319.         argv[argc] = s;
  320.         if (++argc >= MAXPARAM) goto ParsingDone;
  321.         while (*++s != ' ') if (*s == '\0') goto ParsingDone;
  322.         *s = '\0';
  323.         while (*++s == ' ');
  324.     }
  325.     }
  326. ParsingDone:
  327.     return TRUE;
  328. } /* WinInit */
  329.  
  330. /* SetFrameCaption: sets the frame window's text according to the app Id */
  331. /* ===============                                                       */
  332.  
  333. static void PASCAL  SetFrameCaption (void)
  334. {
  335.     char    text[sizeof(PROGNAME)+19];
  336.     char    *t;
  337.     int     Id;
  338.  
  339.     strcpy (text, ProgName);
  340.     Id = GetWindowWord (hFrameWnd, GWW_FRMID);
  341.     if (Id) {
  342.     for (t = text; *t != '\0'; t++) ;   /* look for the end of text */
  343.     *t++ = ' ';
  344.     *t++ = '#';
  345.     itoa (Id, t, 10);   /* append the App Id */
  346.     }
  347.     SetWindowText (hFrameWnd, text);
  348.     if (IsIconic(hFrameWnd))  /* force redrawing of the icon title */
  349.     SendMessage(hFrameWnd, WM_NCACTIVATE, TRUE, 0L);
  350. } /* SetFrameCaption */
  351.  
  352. /* BroadcastEnumProc:   used by EmacsBroadcast */
  353. /* =================                           */
  354. BOOL EXPORT FAR PASCAL BroadcastEnumProc (HWND hWnd, LONG lParam)
  355. {
  356.     char    ClassName [sizeof(FrameClassName)+1];
  357.     UINT    RetVal;
  358.     
  359.     if (hWnd != hFrameWnd) {
  360.         ClassName[0] = '\0';
  361.         GetClassName (hWnd, (LPSTR)&ClassName[0], sizeof(FrameClassName)+1);
  362.         if (strcmp (ClassName, FrameClassName) == 0) {
  363.             /* The enumerated window is a MicroEMACS frame */
  364.             if (lParam != 0) {
  365.                 /*-compute max of all returned values */
  366.                 RetVal = SendMessage (hWnd, EmacsBroadcastMsg,
  367.                                       (UINT)hFrameWnd, lParam);
  368.         BroadcastVal = max(BroadcastVal, RetVal);
  369.         }
  370.         else {
  371.         /*-compute number of applications */
  372.         BroadcastVal++;
  373.         }
  374.     }
  375.     }
  376.     return TRUE;
  377. } /* BroadcastEnumProc */
  378.  
  379. /* EmacsBroadcast:  send a broadcast message to all Emacs applications */
  380. /* ==============                                                      */
  381.  
  382. static DWORD PASCAL   EmacsBroadcast (DWORD MsgParam)
  383.  
  384. /* If MsgParam is not zero, the broadcast is sent as an EmacsBroadcastMsg
  385.    to all the Emacs frame windows, except the one specified by hFrameWnd.
  386.    The wParam of that message is hFrameWnd, lParam is set to MsgParam.
  387.    The value returned is the highest returned value from the broadcast
  388.    or zero if no other emacs instance was found.
  389.  
  390.  - If MsgParam is 0, this function simply returns the number of Emacs
  391.    instances found, not counting the one identified by hFrameWnd.
  392.  
  393. */
  394. {
  395.     FARPROC ProcInstance;
  396.  
  397.     ProcInstance = MakeProcInstance ((FARPROC)BroadcastEnumProc,
  398.                      hEmacsInstance);
  399.     BroadcastVal = 0;
  400.     EnumWindows (ProcInstance, MsgParam);
  401.     FreeProcInstance (ProcInstance);
  402.     return BroadcastVal;
  403. } /* EmacsBroadcast */
  404.  
  405. /* MDIClientSubProc:    Subclassing window proc for the MDI Client window */
  406. /* ================                                                       */
  407.  
  408. LONG EXPORT FAR PASCAL MDIClientSubProc (HWND hWnd, UINT wMsg, UINT wParam,
  409.                          LONG lParam)
  410. {
  411.     switch (wMsg) {
  412.  
  413. #if !WIN30SDK
  414.     case WM_CREATE:
  415.         if (Win31API) DragAcceptFiles (hWnd, TRUE);
  416.         goto DefaultProc;
  417.  
  418.     case WM_DROPFILES:
  419.     DropMessage (hWnd, (HDROP)wParam);
  420.     break;
  421. #endif
  422.  
  423.     case WM_SETCURSOR:
  424.     if (UpdateCursor (hWnd, wParam, lParam)) return TRUE;
  425.     goto DefaultProc;
  426.  
  427.     case WM_KEYDOWN:
  428.     case WM_SYSKEYDOWN:
  429.     case WM_CHAR:
  430.     case WM_SYSCHAR:
  431.     if (EatKey (wMsg, wParam, lParam)) break;
  432.         goto DefaultProc;
  433.  
  434.     case WM_PARENTNOTIFY:
  435.         if (notquiescent) break;    /* blocks mouse selection of an MDI
  436.                        child (except on caption clicks!)
  437.                        */
  438.         goto DefaultProc;
  439.         
  440.     default:
  441. DefaultProc:
  442.     return CallWindowProc (MDIClientProc, hWnd, wMsg, wParam, lParam);
  443.     }
  444.     return 0L;
  445. } /* MDIClientSubProc */
  446.  
  447. /* FrameInit:   Frame window's WM_CREATE */
  448. /* =========                             */
  449.  
  450. void FAR PASCAL FrameInit (CREATESTRUCT *cs)
  451. {
  452.     RECT    Rect;
  453.     CLIENTCREATESTRUCT  ccs;
  454.     HMENU   hMenu;
  455.     UINT    FrameId;
  456.  
  457. #if !WIN30SDK
  458.     if (Win31API) DragAcceptFiles (hFrameWnd, TRUE);
  459. #endif
  460.  
  461.     /*-advertise our birth to other Emacs instances and set the frame's
  462.        caption with an Id if necessary */
  463.     if ((FrameId = (UINT)EmacsBroadcast (MAKELONG(EMACS_STARTING,
  464.                                                   EMACS_PROCESS))) != 0) {
  465.         FrameId++;
  466.     }
  467.     SetWindowWord (hFrameWnd, GWW_FRMID, (WORD)FrameId);
  468.     SetFrameCaption ();
  469.  
  470.     /*-add "optimize" to system menu */
  471.     hMenu = GetSystemMenu (hFrameWnd, 0);
  472.     InsertMenu (hMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED, SC_OPTIMIZE,
  473.                 "&Optimize");
  474.     
  475.     /*-initialize fonts and Cell Metrics */
  476.     FontInit ();
  477.  
  478.     /*-Create the MDI Client window */
  479.     hMDIClientWnd = NULL;   /* the NULL initial value allows
  480.                    DefFrameProc to behave properly if
  481.                    FrameInit fails */
  482.     ccs.hWindowMenu = GetScreenMenuHandle ();
  483.     ccs.idFirstChild = IDM_FIRSTCHILD;
  484.     GetClientRect (hFrameWnd, &Rect);
  485.     hMDIClientWnd = CreateWindow ("MDICLIENT",      /* class */
  486.                   NULL,             /* title */
  487.                   WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL |
  488.                       WS_HSCROLL | WS_VISIBLE,  /* style */
  489.                       0,                   /* positions */
  490.                   0,
  491.                   Rect.right,          /* dimensions */
  492.                   Rect.bottom - EmacsCM.MLHeight,
  493.                   hFrameWnd,           /* parent handle */
  494.                   NULL,                /* menu */
  495.                   hEmacsInstance,
  496.                   (LPSTR) (LPCLIENTCREATESTRUCT) &ccs);
  497.     if (hMDIClientWnd) {
  498.         /* we subclass the MDIClient */
  499.         FARPROC ProcInstance;
  500.  
  501.         MDIClientProc = (WNDPROC)GetWindowLong (hMDIClientWnd, GWL_WNDPROC);
  502.         ProcInstance = MakeProcInstance ((FARPROC)MDIClientSubProc,
  503.                          hEmacsInstance);
  504.         SetWindowLong (hMDIClientWnd, GWL_WNDPROC, (DWORD)ProcInstance);
  505.     }
  506. } /* FrameInit */
  507.  
  508. /* CloseEmacs:   handle WM_CLOSE of WM_QUERYENDSESSION messages */
  509. /* ==========                                                   */
  510.  
  511. static BOOL  PASCAL CloseEmacs (UINT wMsg)
  512.  
  513. /* returns TRUE if emacs should exit */
  514. {
  515.     if (eexitflag) return TRUE;     /* emacs has already quited */
  516.     if (wMsg == WM_QUERYENDSESSION) SystemModal = TRUE;
  517.     if (fbusy != FWRITING) {   /* no file write in progress */
  518.         quit (FALSE, 0);    /* check if it's ok with emacs to terminate */
  519.     }
  520.     else {                  /* there is, indeed a file write in progress
  521.         MessageBeep (0);    /* wake the user up! */
  522.         if (MessageBox (hFrameWnd, TEXT333,
  523.                             /* "File write in progress. Quit later!" */
  524.                         ProgName,
  525.                         MB_OKCANCEL | MB_ICONSTOP |
  526.                 (SystemModal ? MB_SYSTEMMODAL : 0)) == IDCANCEL) {
  527.             quit (TRUE, 0); /* give up! */
  528.         }
  529.     }
  530.     if (wMsg == WM_QUERYENDSESSION) SystemModal = FALSE;
  531.     if (eexitflag) {        /* emacs agrees to quit */
  532.         ClipboardCleanup ();    /* close clipboard if we had it open */
  533.         if (fbusy != FALSE) ffclose (); /* cleanup file IOs */
  534.         return TRUE;
  535.     }
  536.     update (TRUE);          /* restore the caret that quit() moved into
  537.                    the message line */
  538.     return FALSE;        /* we refuse to die! */
  539. } /* CloseEmacs */
  540.  
  541. /* ScrWndProc:  MDI child (screen) window function */
  542. /* ==========                                      */
  543. LONG EXPORT FAR PASCAL ScrWndProc (HWND hWnd, UINT wMsg, UINT wParam,
  544.                    LONG lParam)
  545. {
  546.     switch (wMsg) {
  547.  
  548.     case WM_CREATE:
  549. #if !WIN30SDK
  550.         if (Win31API) DragAcceptFiles (hWnd, TRUE);
  551. #endif
  552.         if (!hscrollbar) ShowScrollBar (hWnd, SB_HORZ, FALSE);
  553.         if (!vscrollbar) ShowScrollBar (hWnd, SB_VERT, FALSE);
  554.     {   /*-init WindowWords for ScrReSize function */
  555.         RECT    Rect;
  556.  
  557.         GetClientRect (hWnd, &Rect);
  558.         SetWindowWord (hWnd, GWW_SCRCX, (WORD)Rect.right);
  559.         SetWindowWord (hWnd, GWW_SCRCY, (WORD)Rect.bottom);
  560.     }
  561.     {   /*-setup the stuff for display.c */
  562.         SCREEN  *sp;
  563.  
  564.         sp = (SCREEN*)(((MDICREATESTRUCT*)(((CREATESTRUCT*)lParam)->
  565.                            lpCreateParams))->lParam);
  566.         SetWindowLong (hWnd, GWL_SCRPTR, (LONG)sp);
  567.         sp->s_drvhandle = hWnd;
  568.     }
  569.     goto DefaultProc;
  570.     
  571.     case WM_DESTROY:
  572.     vtfreescr ((SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR));
  573.     break;
  574.     
  575.     case WM_KILLFOCUS:
  576.     EmacsCaret (FALSE);
  577.     goto DefaultProc;
  578.     case WM_SETFOCUS:
  579.     EmacsCaret (TRUE);
  580.     goto DefaultProc;
  581.     case WM_ACTIVATE:
  582.     if (LOWORD(wParam)) EmacsCaret (TRUE);
  583.     goto DefaultProc;
  584.  
  585.     case WM_MDIACTIVATE:
  586. #if WINDOW_MSWIN32
  587.         if ((HWND)lParam == hWnd) {
  588. #else
  589.     if (wParam) {
  590. #endif
  591.             /* this one is becoming active */
  592.         if (!InternalRequest) {
  593.             InternalRequest = TRUE;
  594.             select_screen ((SCREEN *) GetWindowLong (hWnd, GWL_SCRPTR),
  595.                                FALSE);
  596.         InternalRequest = FALSE;
  597.         }
  598.         else {
  599.         SCREEN  *sp;
  600.  
  601.         sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
  602.         if (sp->s_virtual == NULL) {
  603.             /* this is initialization time! */
  604.             vtinitscr (sp, DisplayableRows (hWnd, 0, &EmacsCM),
  605.                        DisplayableColumns (hWnd, 0, &EmacsCM));
  606.         }
  607.         }
  608.     }
  609.     goto DefaultProc;
  610.     
  611.     case WM_KEYDOWN:
  612.     case WM_SYSKEYDOWN:
  613.     case WM_CHAR:
  614.     case WM_SYSCHAR:
  615.     if (EatKey (wMsg, wParam, lParam)) break;
  616.         goto DefaultProc;
  617.  
  618. #if !WIN30SDK
  619.     case WM_DROPFILES:
  620.         DropMessage (hWnd, (HDROP)wParam);
  621.         break;
  622. #endif
  623.  
  624.     case WM_MOUSEMOVE:
  625.     case WM_LBUTTONDOWN:
  626.     case WM_LBUTTONUP:
  627.     case WM_MBUTTONDOWN:
  628.     case WM_MBUTTONUP:
  629.     case WM_RBUTTONDOWN:
  630.     case WM_RBUTTONUP:
  631.     MouseMessage (hWnd, wMsg, wParam, lParam);
  632.     break;
  633.  
  634.     case WM_VSCROLL:
  635.     case WM_HSCROLL:
  636.         ScrollMessage (hWnd, wMsg, LOWORD(wParam),
  637. #if WINDOW_MSWIN32
  638.                        (int)HIWORD(wParam));
  639. #else
  640.                        (int)LOWORD(lParam));
  641. #endif
  642.         break;
  643.  
  644.     case WM_SETCURSOR:
  645.     if (UpdateCursor (hWnd, wParam, lParam)) return TRUE;
  646.     goto DefaultProc;
  647.  
  648.     case WM_PAINT:
  649.     ScrPaint (hWnd);
  650.     break;
  651.     
  652.     case WM_SIZE:
  653.     if (!IsIconic (hFrameWnd)) {
  654.         if (wParam == SIZEICONIC) EmacsCaret (FALSE);
  655.         else EmacsCaret (TRUE);
  656.         ScrReSize (hWnd, wParam, LOWORD(lParam), HIWORD(lParam));
  657.     }
  658.     goto DefaultProc;
  659.  
  660.     case WM_GETMINMAXINFO:
  661.     GetMinMaxInfo (hWnd, (LPPOINT)lParam);
  662.     goto DefaultProc;
  663.     
  664.     case WM_MOUSEACTIVATE:
  665.     if (notquiescent) break;    /* blocks mouse selection of an MDI
  666.                        child's caption */
  667.     goto DefaultProc;
  668.  
  669.     case WM_SYSCOMMAND:
  670.     switch (wParam & 0xFFF0) {
  671.     case SC_RESTORE:
  672.     case SC_SIZE:
  673.     case SC_MAXIMIZE:
  674.     case SC_MINIMIZE:
  675.     case SC_NEXTWINDOW:
  676.     case SC_PREVWINDOW:
  677.         if (notquiescent) break;    /* sizing commands disabled */
  678.         goto DefaultProc;
  679.     case SC_CLOSE:
  680.         if (!notquiescent) {
  681.         SCREEN  *sp;
  682.  
  683.         /* this must be done here, before any MDI mumbo-jumbo */
  684.             sp = (SCREEN*)GetWindowLong (hWnd, GWL_SCRPTR);
  685.         if (sp == first_screen) {
  686.             cycle_screens (FALSE, 0);
  687.                 }
  688.             if (sp != first_screen) {
  689.                 unlist_screen (sp);
  690.                     free_screen (sp);
  691.         }
  692.         update (TRUE);  /* to restore the caret */
  693.         }
  694.         break;
  695.     case SC_MOVE:
  696.         if (notquiescent) {
  697.             /* prevent moving an MDI client other than the already
  698.            active one, since it would transfer the activation */
  699.             if (hWnd != (HWND)SendMessage (hMDIClientWnd, WM_MDIGETACTIVE,
  700.                                0, 0L)) break;
  701.         }
  702.         goto DefaultProc;
  703.     default:
  704.         goto DefaultProc;
  705.     }
  706.     break;
  707.  
  708.     default:
  709. DefaultProc:
  710.     return DefMDIChildProc (hWnd, wMsg, wParam, lParam);
  711.     }
  712.     return 0L;
  713. } /* ScrWndProc */
  714.  
  715. /* FrameWndProc:    frame window function */
  716. /* ============                           */
  717. LONG EXPORT FAR PASCAL FrameWndProc (HWND hWnd, UINT wMsg, UINT wParam,
  718.                      LONG lParam)
  719. {
  720.     switch (wMsg) {
  721.         
  722. #if !WIN30SDK
  723.     case WM_DROPFILES:
  724.     DropMessage (hWnd, (HDROP)wParam);
  725.     break;
  726. #endif
  727.  
  728.     case WM_INITMENUPOPUP:
  729.     InitMenuPopup ((HMENU)wParam, lParam);
  730.     goto DefaultProc;
  731.  
  732.     case WM_MENUCHAR:
  733.     {
  734.         LONG    Code;
  735.         
  736.         Code = DefFrameProc (hWnd, hMDIClientWnd, wMsg, wParam, lParam);
  737.         if (HIWORD(Code) != 0) return Code; /* matches a menu command */
  738.         if (EatKey (wMsg, wParam, lParam)) return MAKELONG(0,1);
  739.             /* MicroEMACS ate that character, close the current menu! */
  740.         else return Code;
  741.     }
  742.  
  743.     case WM_COMMAND:
  744.         if (!MenuCommand (wParam, lParam)) goto DefaultProc;
  745.     break;
  746.  
  747.     case WM_SYSCOMMAND:
  748.     switch (wParam & 0xFFF0) {
  749.     case SC_RESTORE:
  750.         if (IsIconic (hFrameWnd)) goto DefaultProc;
  751.     case SC_SIZE:
  752.     case SC_MAXIMIZE:
  753.         if (notquiescent) break;    /* sizing commands disabled */
  754.         goto DefaultProc;
  755.     case SC_OPTIMIZE:
  756.         if (notquiescent) break;
  757.         if (IsZoomed (hFrameWnd) || IsIconic (hFrameWnd)) {
  758.             /* must restore it first */
  759.             ShowWindow (hFrameWnd, SW_RESTORE);
  760.         }
  761.         {
  762.             int     IconHeight;
  763.             
  764. #if WIN30SDK
  765.                 {
  766. #else
  767.             if (Win31API) {
  768.                 IconHeight = GetSystemMetrics (SM_CYICONSPACING);
  769.             }
  770.             else {
  771. #endif
  772.                 IconHeight = GetSystemMetrics (SM_CYICON) +
  773.                      GetSystemMetrics (SM_CYCAPTION) +
  774.                              GetSystemMetrics (SM_CYBORDER);
  775.             }
  776.             MoveWindow (hFrameWnd, 0, 0,
  777.                         GetSystemMetrics (SM_CXSCREEN),
  778.                         GetSystemMetrics (SM_CYSCREEN) - IconHeight,
  779.                         TRUE);
  780.         }
  781.         break;
  782.     default:
  783.         goto DefaultProc;
  784.     }
  785.     break;
  786.     
  787.     case WM_SETCURSOR:
  788.     if (UpdateCursor (hWnd, wParam, lParam)) return TRUE;
  789.     goto DefaultProc;
  790.  
  791.     case WM_TIMER:
  792.     if (wParam == T_SLEEP) {
  793.         --Sleeping;     /* signal wake up to TakeANap() */
  794.         break;
  795.     }
  796.     goto DefaultProc;
  797.     
  798.     case WM_PAINT:  /* must be the message line */
  799.     MLPaint ();
  800.     break;
  801.  
  802.     case WM_SIZE:   /* must adjust the MDIClient's size to fit the
  803.                Message Line */
  804.     MoveWindow (hMDIClientWnd, 0, 0,
  805.                     LOWORD(lParam), HIWORD(lParam) - EmacsCM.MLHeight, TRUE);
  806.         InvalidateRect (hFrameWnd, NULL, FALSE);
  807.         break;
  808.  
  809. #if !WINDOW_MSWIN32
  810.     case WM_NCLBUTTONDBLCLK:
  811.     if (Win31API) goto DefaultProc;
  812.     else {
  813.            /* This corrects a Windows 3.0 bug that prevents a maximized
  814.            child from being closed by double-clicking the close-box.
  815.            See "Windows 3: A Developer's Guide" by Jeffrey M.
  816.            Richter, page 480 */
  817.         RECT    Rect;
  818.         HBITMAP hBmp;
  819.         BITMAP  Bmp;
  820.         LONG    MDIGetActivateResult;
  821.         
  822.         if (wParam != HTMENU) goto DefaultProc;
  823.         MDIGetActivateResult = SendMessage (hMDIClientWnd,
  824.                         WM_MDIGETACTIVE, 0, 0);
  825.         if (!HIWORD(MDIGetActivateResult)) goto DefaultProc;
  826.         GetWindowRect (hWnd, &Rect);
  827.         Rect.top += GetSystemMetrics(SM_CYCAPTION) +
  828.             GetSystemMetrics (SM_CYFRAME);
  829.         Rect.left += GetSystemMetrics(SM_CXFRAME);
  830.         hBmp = LoadBitmap (NULL, MAKEINTRESOURCE(OBM_CLOSE));
  831.         GetObject (hBmp, sizeof(BITMAP), (LPSTR)(LPBITMAP)&Bmp);
  832.         Rect.bottom = Rect.top + Bmp.bmHeight;
  833.         Rect.right = Rect.left + Bmp.bmWidth / 2;
  834.         if (!PtInRect(&Rect, MAKEPOINT(lParam))) goto DefaultProc;
  835.         SendMessage(LOWORD(MDIGetActivateResult), WM_SYSCOMMAND,
  836.                         SC_CLOSE, lParam);
  837.     }  
  838.     break;
  839. #endif
  840.  
  841.     case WM_ACTIVATE:
  842.     EmacsCaret (LOWORD(wParam));
  843.         /* This call matters only when the active MDI Child is
  844.            iconic, in which case it never receives the focus and
  845.            thus fails to create the caret (that should still appear
  846.            if we are getting input on the message line) */
  847.     goto DefaultProc;
  848.  
  849.     case WM_CREATE:
  850.     hFrameWnd = hWnd;
  851.     FrameInit ((CREATESTRUCT *)lParam);
  852.     goto DefaultProc;
  853.     
  854.     case WM_QUERYENDSESSION:
  855.     if (CloseEmacs (wMsg)) return 1L;
  856.     break;
  857.     
  858.     case WM_CLOSE:
  859.         if (CloseEmacs (wMsg)) goto DefaultProc;
  860.     break;
  861.     
  862.     case WM_DESTROY:
  863.     if (MainHelpUsed) WinHelp (hFrameWnd, MainHelpFile, HELP_QUIT, NULL);
  864.     if (HelpEngineFile[0] != '\0') WinHelp (hFrameWnd, HelpEngineFile,
  865.                                                 HELP_QUIT, NULL);
  866.     EmacsBroadcast (MAKELONG(EMACS_ENDING,EMACS_PROCESS));
  867.     PostQuitMessage (0);
  868.     break;
  869.     
  870.     default:
  871.         if ((wMsg == EmacsBroadcastMsg) &&
  872.         (HIWORD(lParam) == EMACS_PROCESS)) {
  873.         UINT    Id;
  874.         
  875.             switch (LOWORD(lParam)) {
  876.         case EMACS_STARTING:
  877.             /* another instance of emacs is starting. If our ID was
  878.                0, it should now be 1 */
  879.             if (GetWindowWord (hFrameWnd, GWW_FRMID) == 0) {
  880.                 SetWindowWord (hFrameWnd, GWW_FRMID, 1);
  881.                 SetFrameCaption ();
  882.             }
  883.             break;
  884.         case EMACS_ENDING:
  885.             /* another instance of emacs is ending. If we are going to be
  886.                alone, let's switch our Id back to 0 */
  887.             Id = (UINT)EmacsBroadcast (0);
  888.             /* Id here is the number of Emacs applications, except for us */
  889.             if (Id == 1) {
  890.             SetWindowWord (hFrameWnd, GWW_FRMID, 0);
  891.             SetFrameCaption ();
  892.             }
  893.         }
  894.         /* send back our Id */
  895.         return (LONG)GetWindowWord (hFrameWnd, GWW_FRMID);
  896.         }
  897. DefaultProc:
  898.     return DefFrameProc (hWnd, hMDIClientWnd, wMsg, wParam, lParam);
  899.     }
  900.     return 0L;
  901. } /* FrameWndProc */
  902.  
  903. /* WinMain: Application entry point */
  904. /* =======                          */
  905.  
  906. int PASCAL  WinMain (HANDLE hInstance, HANDLE hPrevInstance,
  907.                      LPSTR lpCmdLine, int nCmdShow)
  908. {
  909.     hEmacsInstance = hInstance;
  910.     if (!WinInit (lpCmdLine, nCmdShow)) return -1;
  911.  
  912.     switch (Catch (ExitCatchBuf)) {
  913.     case 0:
  914.     emacs (argc, argv);
  915.     /* If we exit through an emacs command, we pass here. Otherwise
  916.        (exit through a close in system menu or a windows session
  917.        termination), the application terminates from the call to
  918.        mswgetc that received the signal */
  919.     case ABORT:
  920.         eexitflag = TRUE;
  921.         longop (FALSE);
  922.     PostMessage (hFrameWnd, WM_CLOSE, 0, 0L);
  923.     for (;;) MessageLoop (TRUE);/* go into message loop until WM_QUIT */
  924. /*  case TRUE:
  925.         break; */
  926.     }
  927.     return eexitval;
  928. } /* WinMain */
  929.  
  930. /* ModifyCursor:    forces a WM_SETCURSOR */
  931. /* ============                           */
  932.  
  933. static void PASCAL  ModifyCursor (void)
  934. {
  935.     POINT   pt;
  936.  
  937.     GetCursorPos (&pt);
  938.     SetCursorPos (pt.x, pt.y);
  939. } /* ModifyCursor */
  940.  
  941. /* MessageLoop: Main message loop */
  942. /* ===========                    */
  943.  
  944. static void  PASCAL MessageLoop (BOOL WaitMode)
  945.  
  946. /* If WaitMode is TRUE this function uses GetMessage the first time and
  947.    PeekMessage after that, until the input queue is empty and there is
  948.    something in the in_put pipe. Thus, WM_PAINTs are processed and the
  949.    input queue is emptied into the in_put pipe.
  950.    If WaitMode is FALSE, this function uses PeekMessage from the
  951.    beginning and returns only when the input queue is empty. In that
  952.    case, WM_PAINTs are not processed, but the processor still gets
  953.    relinquished. Note that while peeking for messages, this function
  954.    returns if the in_put pipe is about to overflow */
  955. {
  956.     MSG     Msg;
  957.     BOOL    Peeking;
  958.  
  959.     ++notquiescent; /* this restrics some actions in nested calls */
  960.  
  961.     ScrollBars ();  /* hide or show as needed (may generate WM_SIZE) */
  962.     ModifyCursor ();
  963.  
  964.     do {
  965.         Peeking = !WaitMode;
  966.         for (;;) {
  967.             if(defferupdate) {
  968.                 ShowEmacsCaret (FALSE);
  969.                 update (TRUE);
  970.                 ShowEmacsCaret (TRUE);
  971.             }
  972.         if (Peeking) {
  973.             if (!in_room (5)) break; /* input stream about to overflow */
  974.             if (!PeekMessage (&Msg, NULL, 0, 0, PM_REMOVE)) break;
  975.         }
  976.         else {
  977.                 longop (FALSE); /* we are going to wait for input */
  978.             GetMessage (&Msg, NULL, 0, 0);
  979.             Peeking = TRUE; /* from now on... */
  980.         }
  981.         if (Msg.message == WM_QUIT) {   /* time to leave... */
  982.             JettisonFarStorage ();
  983.             if (hEmacsFont) DeleteObject (hEmacsFont);
  984.             Throw (ExitCatchBuf, TRUE);
  985.             /* we're gone out of business ! */
  986.             /* **************************** */
  987.         }
  988.         if (!TranslateMDISysAccel (hMDIClientWnd, &Msg)) {
  989.             TranslateMessage (&Msg);
  990.             DispatchMessage (&Msg);
  991.         }
  992.     }
  993.     } while (WaitMode && !in_check ()); /* keep waiting if pipe is empty */
  994.  
  995.     --notquiescent;
  996.     ModifyCursor ();
  997.     return;
  998. } /* MessageLoop */
  999.  
  1000. /* GetInput: wait for user input (called by mswgetc from mswdrv.c) */
  1001. /* ========                                                        */
  1002.  
  1003. /* The returned value is the next character from the input stream */
  1004.  
  1005. int FAR PASCAL GetInput (void)
  1006. {
  1007.     if (!in_check ()) {
  1008.     ShowEmacsCaret (TRUE);
  1009.     MessageLoop (TRUE);
  1010.     ShowEmacsCaret (FALSE);
  1011.     }
  1012.     return in_get ();       /* return next character */
  1013. } /* GetInput */
  1014.  
  1015. /* TakeANap:    put emacs to sleep for a few milliseconds */
  1016. /* ========                                               */
  1017.  
  1018. int FAR PASCAL  TakeANap (int t)
  1019. /* this function is used by mswsleep(). It returns TRUE unless the timer
  1020.    could not be created. Note that for a null time, it simply
  1021.    relinquishes the processor */
  1022. {
  1023.     TakingANap = TRUE;
  1024.     if ((t == 0) ||
  1025.         (!SetTimer (hFrameWnd, T_SLEEP, t, (FARPROC)NULL))) {
  1026.     ShowEmacsCaret (TRUE);
  1027.     MessageLoop (FALSE);    /* let's do one relinquish anyway */
  1028.     ShowEmacsCaret (FALSE);
  1029.     TakingANap = FALSE;
  1030.     if (t == 0) return TRUE;
  1031.         else return FALSE;
  1032.     }
  1033.     ShowEmacsCaret (TRUE);
  1034.     Sleeping = 1;   /* this gets reset by WM_TIMER processing */
  1035.     do {
  1036.         MessageLoop (FALSE);
  1037.         WaitMessage ();
  1038.     } while (Sleeping > 0);
  1039.     ShowEmacsCaret (FALSE);
  1040.     KillTimer (hFrameWnd, T_SLEEP);
  1041.     TakingANap = FALSE;
  1042.     return TRUE;
  1043. } /* TakeANap */
  1044.  
  1045. /* UpdateCursor:    sets the apropriate Emacs cursor shape */
  1046. /* ============                                            */
  1047.  
  1048. static BOOL  PASCAL UpdateCursor (HWND hWnd, UINT wParam, LONG lParam)
  1049.  
  1050. /* this function should be called on each WM_SETCURSOR message, to
  1051.    display the appropriate cursor. It returns TRUE if all processing has
  1052.    been done (the calling window function should return TRUE). it
  1053.    returns FALSE if the default window proc sould be called. */
  1054. {
  1055.     HCURSOR hCursor;
  1056.  
  1057.     switch (LOWORD(lParam)) {   /* hit-test area code as in WM_NCHITTEST */
  1058.     case HTCLIENT:
  1059.         if (HourglassOn) hCursor = hHourglass;
  1060.     else {
  1061.         if ((HWND)wParam == hFrameWnd) {
  1062.             if (notquiescent) hCursor = hNotQuiescentCursor;
  1063.             else hCursor = NULL;
  1064.         }
  1065.         else {  /* a screen window or the MDIClient */
  1066.         if (notquiescent) hCursor = hNotQuiescentCursor;
  1067.         else if ((HWND)wParam == hMDIClientWnd) {
  1068.             hCursor = NULL;
  1069.         }
  1070.         else if (mouseflag) {
  1071.             if (MouseTracking) hCursor = hTrackCursor;
  1072.             else hCursor = hScreenCursor;
  1073.         }
  1074.         else hCursor = 0;
  1075.         }
  1076.     }
  1077.     if (!hCursor) hCursor = LoadCursor (NULL, IDC_ARROW);
  1078.     break;
  1079.     case HTHSCROLL:
  1080.     case HTVSCROLL:
  1081.     case HTREDUCE:
  1082.         if (((HWND)wParam == hMDIClientWnd) ||
  1083.             ((HWND)wParam == hFrameWnd)) return FALSE;
  1084.     case HTBOTTOM:
  1085.     case HTBOTTOMLEFT:
  1086.     case HTBOTTOMRIGHT:
  1087.     case HTLEFT:
  1088.     case HTRIGHT:
  1089.     case HTTOP:
  1090.     case HTTOPLEFT:
  1091.     case HTTOPRIGHT:
  1092.     case HTSIZE:
  1093.     case HTZOOM:
  1094.         /* all those are unuseable when notquiescent */
  1095.         if (notquiescent) {
  1096.             if (HourglassOn) hCursor = hHourglass;
  1097.         else hCursor = hNotQuiescentCursor;
  1098.         }
  1099.         else return FALSE;
  1100.     break;
  1101.     default:
  1102.     return FALSE;
  1103.     }
  1104.     SetCursor (hCursor);
  1105.     return TRUE;
  1106. } /* UpdateCursor */
  1107.  
  1108. /* SetHourglass:    sets or removes the hourglass cursor */
  1109. /* ============                                          */
  1110.  
  1111. static void  PASCAL SetHourglass (BOOL hg)
  1112.  
  1113. /* hg = TRUE sets the hourglass, hg = FALSE removes it */
  1114. {
  1115.     POINT   Point;
  1116.     HWND    hWnd;
  1117. #if GRINDERS != 0
  1118.     static  int PrevGrinderIndex = -1;
  1119. #endif
  1120.  
  1121.     if (hg == HourglassOn) {
  1122. #if GRINDERS == 0
  1123.         return;   /* nothing to do */
  1124. #else
  1125.         if ((hg == FALSE) || (PrevGrinderIndex == GrinderIndex)) return;
  1126.         PrevGrinderIndex = GrinderIndex;
  1127. #endif
  1128.     }
  1129.     HourglassOn = hg;
  1130.     GetCursorPos (&Point);
  1131.     hWnd = WindowFromPoint (Point);
  1132.     if (hWnd && ((hWnd == hFrameWnd) || IsChild (hFrameWnd, hWnd))) {
  1133.         /* the cursor is above one of MicroEMACS's windows */
  1134. #if GRINDERS != 0
  1135.         if (flickcode) {
  1136.             hHourglass = hRealHourglass;
  1137.     }
  1138.         else {
  1139.             hHourglass = GrinderCursor[GrinderIndex];
  1140.         }
  1141. #endif
  1142.     SetCursorPos (Point.x, Point.y);
  1143.     /* this should cause a dummy cursor movement to be processed,
  1144.        ending up in UpdateCursor being invoked */
  1145.     }
  1146. } /* SetHourglass */
  1147.